package genedit.controller;

import genedit.model.graph.BreakPoint;
import genedit.model.graph.ConnectionPoint;
import genedit.model.graph.GraphElement;
import genedit.model.graph.Link;
import genedit.view.graph.ElementPainter;
import genedit.view.graph.LinkPainter;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class MovePointCommand extends Command {

	private double moveByX = 0;
	private double moveByY = 0;
	private double globalMoveByX = 0;
	private double globalMoveByY = 0;
	private boolean useGlobalDiff;
	private Link link = null;

	//neophodni podaci za izvršavanje undo/redo
	private ConnectionPoint oldStartElementConnectionPoint;
	private ConnectionPoint oldEndElementConnectionPoint;
	private ConnectionPoint newStartElementConnectionPoint;
	private ConnectionPoint newEndElementConnectionPoint;
	private List<BreakPoint> oldBreakPoints;
	private List<BreakPoint> newBreakPoints;
	
	private static List<BreakPoint> oldBreakPointsTranslator=null;
	
	private int index = 0;

	protected static void staticReset(){
		oldBreakPointsTranslator=null;
	}

	/** @param isUndoable */
	public MovePointCommand(boolean isUndoable,double globalMoveByX, double globalMoveByY) {
		super(isUndoable);
		this.globalMoveByX=globalMoveByX;
		this.globalMoveByY=globalMoveByY;
		this.useGlobalDiff=false;
		this.oldStartElementConnectionPoint=null;
		this.oldEndElementConnectionPoint=null;
		this.newStartElementConnectionPoint=null;
		this.newEndElementConnectionPoint=null;
		this.oldBreakPoints=null;
		this.newBreakPoints=null;

	}

	public void execute() {
		//ako se radi o ponovnom izvršavanju
		if(useGlobalDiff){
			if(index==0){
				//prevezivanje izmedju starog i novog elementa za početak veze
				link.setStart(newStartElementConnectionPoint);
				GraphElement backup=oldStartElementConnectionPoint.getParent();
				oldStartElementConnectionPoint.getParent().removeConnectionPoints(oldStartElementConnectionPoint);
				oldStartElementConnectionPoint.setParent(backup);
				newStartElementConnectionPoint.getParent().addConnectionPoints(newStartElementConnectionPoint);
			}else if(index==link.getBreakPoints().size() - 1){
				//isto kao i gore važi i ovde za kraj veze
				link.setEnd(newEndElementConnectionPoint);
				GraphElement backup=oldEndElementConnectionPoint.getParent();
				oldEndElementConnectionPoint.getParent().removeConnectionPoints(oldEndElementConnectionPoint);
				oldEndElementConnectionPoint.setParent(backup);
				newEndElementConnectionPoint.getParent().addConnectionPoints(newEndElementConnectionPoint);
			}
			
			//ako se promenio broj breakpointa
			if(index==oldBreakPoints.size()-1)
				index=newBreakPoints.size()-1;
			
			//vrati nove breakpointe
			link.setBreakPoints(newBreakPoints);
			getDocument().getModel().refresh();
			return;
		}
		
		//ako je ovo prva komanda u nizu sačuvaj početno stanje veze
		if(oldBreakPointsTranslator==null){
			oldBreakPointsTranslator=new ArrayList<BreakPoint>();
			for(BreakPoint bp : link.getBreakPoints()){
				oldBreakPointsTranslator.add(new BreakPoint((Point2D)bp.getPosition().clone()));
			}
		}
		
		//ako se radi o pomeranju breakpointa iscrtaj njegovo pomeranje
		if (index != -1) {
			// transliramo selektovani breakpoint
			double x = ((Link) link).getBreakPoints().get(index).getPosition().getX();
			double y = ((Link) link).getBreakPoints().get(index).getPosition().getY();
			((Link) link).getBreakPoints().get(index).getPosition()
					.setLocation(x + moveByX, y + moveByY);
		}

		// ako je kraj izrade veze
		if (undoable) {
			// ukoliko je pocetni ili krajnji, vrsimo prevezivanje za novi element u novodefinisani connection point
			if (index == 0) { 
				//ako na datoj poziciji postoji element preveži početak veze na njega
				if (nonLinkElement(((Link) link).getBreakPoints().get(index).getPosition()) != null) {
					oldStartElementConnectionPoint = new ConnectionPoint(link.getStart().getPosition());
					oldStartElementConnectionPoint.setParent(link.getStart().getParent());
					oldStartElementConnectionPoint.addStarting(link);
					GraphElement element = nonLinkElement(
							link.getBreakPoints().get(index).getPosition()).getElement();
					element.removeConnectionPoints(link.getStart());
					newStartElementConnectionPoint = new ConnectionPoint(link.getBreakPoints()
							.get(index).getPosition());
					newStartElementConnectionPoint.setParent(element);
					newStartElementConnectionPoint.addStarting(link);
					link.setStart(newStartElementConnectionPoint);
					element.addConnectionPoints(newStartElementConnectionPoint);
					
				}
			} else if (index == ((Link) link).getBreakPoints().size() - 1) {
				//kao gore samo važi za kraj veze
				if (nonLinkElement(((Link) link).getBreakPoints().get(index).getPosition()) != null) {
					oldEndElementConnectionPoint = new ConnectionPoint(link.getEnd().getPosition());
					oldEndElementConnectionPoint.setParent(link.getEnd().getParent());
					oldEndElementConnectionPoint.addEnding(link);
					GraphElement element = nonLinkElement(
							link.getBreakPoints().get(index).getPosition()).getElement();
					element.removeConnectionPoints(link.getEnd());
					newEndElementConnectionPoint = new ConnectionPoint(link.getBreakPoints()
							.get(index).getPosition());
					newEndElementConnectionPoint.setParent(element);
					newEndElementConnectionPoint.addEnding(link);
					link.setEnd(newEndElementConnectionPoint);
					element.addConnectionPoints(newEndElementConnectionPoint);
				}
			}
			
			//ispravi vezu da bude pod pravim uglom
			link.removeIntersectPoints();
			link.rectLink();
			link.addIntersectionPoints();
			
			//sačuvaj izgled nove veze
			oldBreakPoints=oldBreakPointsTranslator;
			oldBreakPointsTranslator=null;
			newBreakPoints=new ArrayList<BreakPoint>();
			for(BreakPoint bp : link.getBreakPoints()){
				newBreakPoints.add(new BreakPoint((Point2D)bp.getPosition().clone()));
			}
			if(index==oldBreakPoints.size()-1)
				index=newBreakPoints.size()-1;
		}
		getDocument().getModel().refresh();
	}

	public void undo() {
		//postavi da se desio prvi unod - informacija sledećem redo da treba da se ponaša na drugi način
		if(!useGlobalDiff){
			moveByX=globalMoveByX;
			moveByY=globalMoveByY;
			useGlobalDiff=true;
		}

		//prevezivanje elemenata za početak i kraj veze
		if (index == 0) {
			link.setStart(oldStartElementConnectionPoint);
			GraphElement backup=newStartElementConnectionPoint.getParent();
			newStartElementConnectionPoint.getParent().removeConnectionPoints(newStartElementConnectionPoint);
			newStartElementConnectionPoint.setParent(backup);
			oldStartElementConnectionPoint.getParent().addConnectionPoints(oldStartElementConnectionPoint);
		} else if (index == link.getBreakPoints().size() - 1) {
			link.setEnd(oldEndElementConnectionPoint);
			GraphElement backup=newEndElementConnectionPoint.getParent();
			newEndElementConnectionPoint.getParent().removeConnectionPoints(newEndElementConnectionPoint);
			newEndElementConnectionPoint.setParent(backup);
			oldEndElementConnectionPoint.getParent().addConnectionPoints(oldEndElementConnectionPoint);
		}
		
		//vraćanje starog izgleda veze
		link.setBreakPoints(oldBreakPoints);
		if(index==newBreakPoints.size()-1)
			index=oldBreakPoints.size()-1;
		getDocument().getModel().refresh();
	}


	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	public Link getLink() {
		return link;
	}

	public void setLink(Link link) {
		this.link = link;
	}

	public double getMoveByX() {
		return moveByX;
	}

	/** @param newMoveByX */
	public void setMoveByX(double newMoveByX) {
		moveByX = newMoveByX;
	}

	public double getMoveByY() {
		return moveByY;
	}

	/** @param newMoveByY */
	public void setMoveByY(double newMoveByY) {
		moveByY = newMoveByY;
	}

	protected ElementPainter nonLinkElement(Point2D position) {
		if (getDocument() != null) {
			Iterator<ElementPainter> iter = getDocument().getIteratorElementPainters();
			while (iter.hasNext()) {
				ElementPainter painter = iter.next();
				if (painter.isElementAt(position) && !(painter instanceof LinkPainter))
					return painter;
			}
		}
		return null;
	}

}
